package games.rolling;

import gps_api.Problem;
import gps_api.Rule;
import gps_api.State;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class RollingProblem implements Problem{
	private int heu = 0;
	static final int HEU_NOTHING=0;
	static final int HEU_BAD_POSITION=1;
	static final int HEU_MANHATTANIMP=2;


	public RollingProblem() throws IOException{
		Properties configFile= new Properties();

		configFile.load(this.getClass().getClassLoader().getResourceAsStream("games\\rolling\\rolling.conf"));
		setHeuristic(configFile.getProperty("heuristic"));
	}


	@Override
	public Double getHeuristic(State state) {
		Double ans;
		switch (heu) {
		case HEU_NOTHING: ans = 0.0;
			break;
		case HEU_BAD_POSITION: ans = getBadPositions(state); break;
		case HEU_MANHATTANIMP: ans = getManhattanImp(state); break;
		default: ans = 0.0;
			break;
		}
		return ans;
	}

	private void setHeuristic(String heuristic){
		if(heuristic.equals("badpos")){
			setHeu(HEU_BAD_POSITION);
		}else if (heuristic.equals("nothing")){
			setHeu(HEU_NOTHING);
		}else if (heuristic.equals("manhattanimp")){
			setHeu(HEU_MANHATTANIMP);
		}else{
			System.out.println("Wrong Heuristic input. Check \"rolling.conf\". Set to default");
			setHeu(HEU_NOTHING);
		}
	}

	private Double getBadPositions(State state){
		Double ans=0.0;

		for(int x=0; x<RollingState.SIZE_TABLE; x++)
			for(int y=0; y<RollingState.SIZE_TABLE; y++){
				if(((RollingState)state).table[x][y]!=RollingToken.EMPTY &&
						(((RollingState)state).table[x][y]!=RollingToken.WHITE ||
						((RollingState)state).table[x][y]==RollingToken.WHITE && x==1 && y==1 )){
					ans+=1;
				}
			}

		return ans;
	}

	private Double getManhattanImp(State state) {
		Double ans = 0.0;
		for(int x=0; x<RollingState.SIZE_TABLE; x++)
			for(int y=0; y<RollingState.SIZE_TABLE; y++)
				switch (((RollingState)state).table[x][y]) {
				case RollingToken.EMPTY:
					break;
				case RollingToken.BLACK:
					if(x== 1 && y ==1){
						ans += 6.0;
					}else {
						ans+= 4.0;
					}
					break;
				case RollingToken.WHITE:
					if (x==1 && y==1){
						ans+= 6.0;
					}
					break;
				case RollingToken.H_BW:
					//antes 9
					if(x == 0) // Border case, needs at least 3 movements
						ans += 5.0;
					else
						ans += 1.0;
					break;
				case RollingToken.H_WB:
					if(x == RollingState.SIZE_TABLE-1) // Border case, needs at least 3 movements
						ans += 5.0;
					else
						ans += 1.0;
					break;
				case RollingToken.V_BW:
					if(y == 0) // Border case, needs at least 3 movements
						ans += 5.0;
					else
						ans += 1.0;
					break;
				case RollingToken.V_WB:
					if(y == RollingState.SIZE_TABLE-1) // Border case, needs at least 3 movements
						ans += 5.0;
					else
						ans += 1.0;
					break;
				default:
					break;				}
		return ans;
	}

	public void setHeu(int heu) {
		this.heu = heu;
	}

	@Override
	public State getInitState() {
		RollingState state = new RollingState();
		for(int x=0; x<RollingState.SIZE_TABLE; x++)
			for(int y=0; y<RollingState.SIZE_TABLE; y++)
				state.table[x][y] = RollingToken.BLACK;

		state.table[1][1] = RollingToken.EMPTY;

		state.setEmptyPos(1, 1);
		return (State)state;
	}

	public State get32Towin(){
		RollingState state = new RollingState();

		state.table[0][0]= RollingToken.BLACK;
		state.table[0][1]= RollingToken.BLACK;
		state.table[0][2]= RollingToken.BLACK;

		state.table[1][0]= RollingToken.H_BW;
		state.table[1][1]= RollingToken.EMPTY;
		state.table[1][2]= RollingToken.BLACK;

		state.table[2][0]= RollingToken.H_WB;
		state.table[2][1]= RollingToken.V_WB;
		state.table[2][2]= RollingToken.BLACK;

		state.setEmptyPos(1, 1);

		return state;
	}

	public State get28Towin(){
		RollingState state = new RollingState();

		state.table[0][0]= RollingToken.EMPTY;
		state.table[0][1]= RollingToken.V_WB;
		state.table[0][2]= RollingToken.V_WB;

		state.table[1][0]= RollingToken.H_BW;
		state.table[1][1]= RollingToken.V_BW;
		state.table[1][2]= RollingToken.H_WB;

		state.table[2][0]= RollingToken.H_WB;
		state.table[2][1]= RollingToken.V_WB;
		state.table[2][2]= RollingToken.BLACK;

		state.setEmptyPos(0, 0);

		return state;
	}

	public State get24Towin(){
		RollingState state = new RollingState();

		state.table[0][0]= RollingToken.WHITE;
		state.table[0][1]= RollingToken.BLACK;
		state.table[0][2]= RollingToken.EMPTY;

		state.table[1][0]= RollingToken.WHITE;
		state.table[1][1]= RollingToken.V_WB;
		state.table[1][2]= RollingToken.H_WB;

		state.table[2][0]= RollingToken.H_WB;
		state.table[2][1]= RollingToken.V_WB;
		state.table[2][2]= RollingToken.BLACK;

		state.setEmptyPos(0, 2);

		return state;
	}


	public State get20Towin(){
		RollingState state = new RollingState();

		state.table[0][0]= RollingToken.EMPTY;
		state.table[0][1]= RollingToken.V_BW;
		state.table[0][2]= RollingToken.BLACK;

		state.table[1][0]= RollingToken.WHITE;
		state.table[1][1]= RollingToken.H_WB;
		state.table[1][2]= RollingToken.WHITE;

		state.table[2][0]= RollingToken.H_WB;
		state.table[2][1]= RollingToken.V_WB;
		state.table[2][2]= RollingToken.BLACK;

		state.setEmptyPos(0, 0);

		return state;
	}

	public State get16Towin(){
		RollingState state = new RollingState();

		state.table[0][0]= RollingToken.H_WB;
		state.table[0][1]= RollingToken.V_BW;
		state.table[0][2]= RollingToken.EMPTY;

		state.table[1][0]= RollingToken.H_WB;
		state.table[1][1]= RollingToken.V_BW;
		state.table[1][2]= RollingToken.WHITE;

		state.table[2][0]= RollingToken.H_WB;
		state.table[2][1]= RollingToken.V_WB;
		state.table[2][2]= RollingToken.BLACK;

		state.setEmptyPos(0, 2);

		return state;
	}

	public State get12Towin(){
		RollingState state = new RollingState();

		state.table[0][0]= RollingToken.H_WB;
		state.table[0][1]= RollingToken.V_BW;
		state.table[0][2]= RollingToken.H_WB;

		state.table[1][0]= RollingToken.H_WB;
		state.table[1][1]= RollingToken.EMPTY;
		state.table[1][2]= RollingToken.H_BW;

		state.table[2][0]= RollingToken.H_WB;
		state.table[2][1]= RollingToken.V_BW;
		state.table[2][2]= RollingToken.WHITE;

		state.setEmptyPos(1, 1);

		return state;
	}

	public State get8Towin(){
		RollingState state = new RollingState();

		state.table[0][0]= RollingToken.H_WB;
		state.table[0][1]= RollingToken.V_BW;
		state.table[0][2]= RollingToken.H_WB;

		state.table[1][0]= RollingToken.BLACK;
		state.table[1][1]= RollingToken.EMPTY;
		state.table[1][2]= RollingToken.H_BW;

		state.table[2][0]= RollingToken.WHITE;
		state.table[2][1]= RollingToken.WHITE;
		state.table[2][2]= RollingToken.WHITE;

		state.setEmptyPos(1, 1);
		return state;
	}

	public State get4Towin(){
		RollingState state = new RollingState();

		state.table[0][0]= RollingToken.WHITE;
		state.table[0][1]= RollingToken.V_WB;
		state.table[0][2]= RollingToken.H_WB;

		state.table[1][0]= RollingToken.WHITE;
		state.table[1][1]= RollingToken.EMPTY;
		state.table[1][2]= RollingToken.H_BW;

		state.table[2][0]= RollingToken.WHITE;
		state.table[2][1]= RollingToken.WHITE;
		state.table[2][2]= RollingToken.WHITE;

		state.setEmptyPos(1, 1);

		return state;
	}


	@Override
	public List<Rule> getRules() {
		List<Rule> rules= new ArrayList<Rule>();

		rules.add(new RollingRule(RollingRule.MOV_UP));
		rules.add(new RollingRule(RollingRule.MOV_RIGHT));
		rules.add(new RollingRule(RollingRule.MOV_LEFT));
		rules.add(new RollingRule(RollingRule.MOV_DOWN));

		return rules;
	}

	@Override
	public boolean isGoal(State state) {
		boolean ans = true;

		for(int x=0; x<RollingState.SIZE_TABLE && ans; x++){
			for(int y=0; y<RollingState.SIZE_TABLE && ans; y++){
				if(((RollingState)state).table[x][y]!=RollingToken.WHITE && ((RollingState)state).table[x][y]!=RollingToken.EMPTY)
					ans = false;
			}
		}
		if(ans && ((RollingState)state).table[1][1] != RollingToken.EMPTY)
			ans = false;
		return ans;
	}



}
